package org.sample.project.custom;

import liquibase.Scope;
import liquibase.changelog.ChangeLogParameters;
import liquibase.changelog.ChangeSet;
import liquibase.exception.ChangeLogParseException;
import liquibase.parser.core.formattedsql.FormattedSqlChangeLogParser;
import liquibase.precondition.core.PreconditionContainer;
import liquibase.precondition.core.TableExistsPrecondition;
import liquibase.util.StringUtil;

import java.util.regex.Matcher;

/**
 * 自定义SQL形式的解析器来支持自定义Precondition
 * Created by foolrabbit on 2025-01-24.
 */
public class MyFormattedSqlChangeLogParser extends FormattedSqlChangeLogParser {

    @Override
    protected void handlePreconditionCase(ChangeLogParameters changeLogParameters,
                                          ChangeSet changeSet,
                                          Matcher preconditionMatcher) throws ChangeLogParseException {
        if (changeSet.getPreconditions() == null) {
            changeSet.setPreconditions(new PreconditionContainer());
        }

        if (preconditionMatcher.groupCount() == 2) {
            String name = StringUtil.trimToNull(preconditionMatcher.group(1));
            if (name != null) {
                String body = preconditionMatcher.group(2).trim();
                switch (name) {
                    case "qs-table-not-exists":
                        Scope.getCurrentScope().getLog(this.getClass()).fine("Matched table-not-exists precondition");
                        changeSet.getPreconditions().addNestedPrecondition(this.parseTableExistsCondition(changeLogParameters.expandExpressions(StringUtil.trimToNull(body), changeSet.getChangeLog())));
                        break;
                    default:
                        super.handlePreconditionCase(changeLogParameters, changeSet, preconditionMatcher);
                }
            }
        }
    }

    private MyTableNotExistsCheckPrecondition parseTableExistsCondition(String body) throws ChangeLogParseException {
        Matcher tableMatcher = this.TABLE_NAME_STATEMENT_PATTERN.matcher(body);
        Matcher schemaMatcher = this.SCHEMA_NAME_STATEMENT_PATTERN.matcher(body);
        MyTableNotExistsCheckPrecondition tableNotExistsPrecondition = new MyTableNotExistsCheckPrecondition();
        if (tableMatcher.matches()) {
            if (tableMatcher.groupCount() == 0) {
                throw new ChangeLogParseException("Table name was not specified in tableExists precondition but is required '" + body + "'.");
            } else if (tableMatcher.groupCount() > 1) {
                throw new ChangeLogParseException("Multiple table names were specified in tableExists precondition '" + body + "'.");
            } else {
                tableNotExistsPrecondition.setTableName(tableMatcher.group(1));
                if (schemaMatcher.matches() && schemaMatcher.groupCount() == 1) {
                    if (schemaMatcher.groupCount() > 1) {
                        throw new ChangeLogParseException("Multiple schema names were specified in tableExists precondition '" + body + "'.");
                    }

                    tableNotExistsPrecondition.setSchemaName(schemaMatcher.group(1));
                }

                return tableNotExistsPrecondition;
            }
        } else {
            throw new ChangeLogParseException("Table name was not specified correctly in tableExists precondition.");
        }
    }
}
